#pragma once

static int maxcap = 5;
// 阻塞队列类
template <class T>
class BlockQueue
{
public:
    BlockQueue(const int gmaxcap = maxcap)
        : _maxcap(gmaxcap)
    {
        // 锁以及条件变量的初始化
        pthread_mutex_init(&_mtx, nullptr);
        pthread_cond_init(&_ccond, nullptr);
        pthread_cond_init(&_pcond, nullptr);
    }
    // 入队,生产者往共享资源中写入数据
    void push(const T &val) // 输入使用引用
    {
        // 加锁保证线程安全
        pthread_mutex_lock(&_mtx);
        // 这里要使用while来判断，因为在线程醒来的时候可能数据还是满的
        while (isfull())
        {
            // 不满足条件，等待
            pthread_cond_wait(&_pcond, &_mtx);
        }
        // 到这里队列一定不是满的，入队
        _q.push(val);
        //这里有数据了，可以叫醒消费者来消费
        pthread_cond_signal(&_ccond); //这里还可以有一定的策略，就是可以在队列1/3的时候再叫醒生产者
        pthread_mutex_unlock(&_mtx);
    }
    // 出列，消费者拿走共享资源中的数据
    void pop(T *val) // 输出使用指针
    {
        // 加锁保证线程安全
        pthread_mutex_lock(&_mtx);
        // 这里要使用while来判断，因为在线程醒来的时候可能数据还是满的
        while (isempty())
        {
            // 不满足条件，等待
            pthread_cond_wait(&_ccond, &_mtx);
        }
        // 到这里队列一定不是满的，入队
        *val = _q.front();
        _q.pop();
        //这里至少有一个位置是空的，可以让生产者生产了
        pthread_cond_signal(&_pcond); //这里还可以有一定的策略，就是可以在队列2/3的时候再叫醒生产者
        pthread_mutex_unlock(&_mtx);
    }
    bool isfull()
    {
        return _q.size() == _maxcap;
    }
    bool isempty()
    {
        return _q.empty();
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mtx);
        pthread_cond_destroy(&_ccond);
        pthread_cond_destroy(&_pcond);
    }

private:
    std::queue<T> _q;           // 阻塞队列
    int _maxcap;           // 自定义的最大
    pthread_mutex_t _mtx;  // 锁
    pthread_cond_t _ccond; // 消费者的条件变量
    pthread_cond_t _pcond; // 生产者的条件变量
};